package com.zhuss.lock;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.zhuss.appconfig.RedisProcessor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * CurrentThreadFactory
 *
 * @Author zhushanshuo
 * @Date 2024-08-15 10:01:59
 */
@Slf4j
@Component
public class CurrentThreadIdGenerator {

    /**
     * 为了实现 分布式 环境下的 可重入锁  设计
     * 虚拟的分布式线程ID ThreadLocal
     * 通过Redis incrBy生成
     */
    private ThreadLocal<Long> simpleIdCluster = new ThreadLocal<>();

    /**
     * 判断当前线程 加锁 和 释放锁 进入的次数
     *
     */
    private Map<Long, Integer> userNumMap = new HashMap<>();

    /**
     * 获取当前线程的分布式虚拟线程ID
     * 当虚拟线程ThreadLocal中没有时，生成后并返回
     * 当ThreadLocal中有时，直接返回
     * @Param 加锁时， use：1  释放锁：use：-1
     * @return
     */
    public Long gen(Integer use) {
        Long realThreadId = Thread.currentThread().getId();
        if (ObjectUtil.isEmpty(userNumMap.get(realThreadId))) {
            userNumMap.put(realThreadId, use);
        } else {
            userNumMap.put(realThreadId, userNumMap.get(realThreadId) + use);
        }
        if (ObjectUtil.isEmpty(simpleIdCluster.get())) {
            simpleIdCluster.set(incr());
        }
        Long currentThreadId = simpleIdCluster.get();
        if (Integer.valueOf(0).equals(userNumMap.get(realThreadId))) {
            // help gc
            simpleIdCluster.remove();
            userNumMap.remove(realThreadId);
        }
        return currentThreadId;
    }

    public void clearThreadId() {
        Long realThreadId = Thread.currentThread().getId();
        Integer num = userNumMap.remove(realThreadId);
        log.info("真实线程ID：{}, 加锁失败, 可重入值: {}", realThreadId, num);
    }

    public Boolean hasLock() {
        Long realThreadId = Thread.currentThread().getId();
        Boolean hasLock = userNumMap.containsKey(realThreadId);
        Integer num = userNumMap.get(realThreadId);
        log.info("当前线程是否还拥有锁，真实线程ID：{}， 是否拥有锁：{}, 可重入次数：{}", realThreadId, hasLock, num);
        return hasLock;
    }

    private Long incr() {
        Long clusterThreadId = SpringUtil.getBean(RedisProcessor.class).genClusterThreadId();
        return clusterThreadId;
    }

}
